home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / src / mail / pine3.96.tar.gz / pine3.96.tar / pine3.96 / imap / ANSI / c-client / tcp_unix.c < prev    next >
C/C++ Source or Header  |  1997-02-21  |  17KB  |  551 lines

  1. /*
  2.  * Program:    UNIX TCP/IP routines
  3.  *
  4.  * Author:    Mark Crispin
  5.  *        Networks and Distributed Computing
  6.  *        Computing & Communications
  7.  *        University of Washington
  8.  *        Administration Building, AG-44
  9.  *        Seattle, WA  98195
  10.  *        Internet: MRC@CAC.Washington.EDU
  11.  *
  12.  * Date:    1 August 1988
  13.  * Last Edited:    21 February 1997
  14.  *
  15.  * Copyright 1997 by the University of Washington
  16.  *
  17.  *  Permission to use, copy, modify, and distribute this software and its
  18.  * documentation for any purpose and without fee is hereby granted, provided
  19.  * that the above copyright notice appears in all copies and that both the
  20.  * above copyright notice and this permission notice appear in supporting
  21.  * documentation, and that the name of the University of Washington not be
  22.  * used in advertising or publicity pertaining to distribution of the software
  23.  * without specific, written prior permission.  This software is made available
  24.  * "as is", and
  25.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  26.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  27.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  28.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  29.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  30.  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  31.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
  32.  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  33.  *
  34.  */
  35.  
  36. #undef write            /* don't use redefined write() */
  37.  
  38.                 /* TCP timeout handler routine */
  39. static tcptimeout_t tcptimeout = NIL;
  40.                 /* TCP timeouts, in seconds */
  41. static long tcptimeout_open = 0;
  42. static long tcptimeout_read = 0;
  43. static long tcptimeout_write = 0;
  44. static long rshtimeout = 15;    /* rsh timeout */
  45.  
  46. /* TCP/IP manipulate parameters
  47.  * Accepts: function code
  48.  *        function-dependent value
  49.  * Returns: function-dependent return value
  50.  */
  51.  
  52. void *tcp_parameters (long function,void *value)
  53. {
  54.   switch ((int) function) {
  55.   case SET_TIMEOUT:
  56.     tcptimeout = (tcptimeout_t) value;
  57.     break;
  58.   case GET_TIMEOUT:
  59.     value = (void *) tcptimeout;
  60.     break;
  61.   case SET_OPENTIMEOUT:
  62.     tcptimeout_open = (long) value;
  63.     break;
  64.   case GET_OPENTIMEOUT:
  65.     value = (void *) tcptimeout_open;
  66.     break;
  67.   case SET_READTIMEOUT:
  68.     tcptimeout_read = (long) value;
  69.     break;
  70.   case GET_READTIMEOUT:
  71.     value = (void *) tcptimeout_read;
  72.     break;
  73.   case SET_WRITETIMEOUT:
  74.     tcptimeout_write = (long) value;
  75.     break;
  76.   case GET_WRITETIMEOUT:
  77.     value = (void *) tcptimeout_write;
  78.     break;
  79.   case SET_RSHTIMEOUT:
  80.     rshtimeout = (long) value;
  81.     break;
  82.   case GET_RSHTIMEOUT:
  83.     value = (void *) rshtimeout;
  84.     break;
  85.   default:
  86.     value = NIL;        /* error case */
  87.     break;
  88.   }
  89.   return value;
  90. }
  91.  
  92. /* TCP/IP open
  93.  * Accepts: host name
  94.  *        contact service name
  95.  *        contact port number
  96.  * Returns: TCP/IP stream if success else NIL
  97.  */
  98.  
  99. TCPSTREAM *tcp_open (char *host,char *service,long port)
  100. {
  101.   TCPSTREAM *stream = NIL;
  102.   int i,sock,flgs;
  103.   char *s;
  104.   struct sockaddr_in sin;
  105.   struct hostent *host_name;
  106.   char hostname[MAILTMPLEN];
  107.   char tmp[MAILTMPLEN];
  108.   fd_set fds,efds;
  109.   struct protoent *pt = getprotobyname ("ip");
  110.   struct servent *sv = service ? getservbyname (service,"tcp") : NIL;
  111.   struct timeval tmo;
  112.   tmo.tv_sec = tcptimeout_open;
  113.   tmo.tv_usec = 0;
  114.   FD_ZERO (&fds);        /* initialize selection vector */
  115.   FD_ZERO (&efds);        /* handle errors too */
  116.   if (s = strchr (host,':')) {    /* port number specified? */
  117.     *s++ = '\0';        /* yes, tie off port */
  118.     port = strtol (s,&s,10);    /* parse port */
  119.     if (s && *s) {
  120.       sprintf (tmp,"Junk after port number: %.80s",s);
  121.       mm_log (tmp,ERROR);
  122.       return NIL;
  123.     }
  124.     sin.sin_port = htons (port);
  125.   }
  126.                 /* copy port number in network format */
  127.   else sin.sin_port = sv ? sv->s_port : htons (port);
  128.  
  129.   /* The domain literal form is used (rather than simply the dotted decimal
  130.      as with other Unix programs) because it has to be a valid "host name"
  131.      in mailsystem terminology. */
  132.                 /* look like domain literal? */
  133.   if (host[0] == '[' && host[(strlen (host))-1] == ']') {
  134.     strcpy (hostname,host+1);    /* yes, copy number part */
  135.     hostname[(strlen (hostname))-1] = '\0';
  136.     if ((sin.sin_addr.s_addr = inet_addr (hostname)) != -1) {
  137.       sin.sin_family = AF_INET;    /* family is always Internet */
  138.       strcpy (hostname,host);    /* hostname is user's argument */
  139.     }
  140.     else {
  141.       sprintf (tmp,"Bad format domain-literal: %.80s",host);
  142.       mm_log (tmp,ERROR);
  143.       return NIL;
  144.     }
  145.   }
  146.   else {            /* lookup host name, note that brain-dead Unix
  147.                    requires lowercase! */
  148.     strcpy (hostname,host);    /* in case host is in write-protected memory */
  149.     i = (int) alarm (0);    /* quell alarms */
  150.     if ((host_name = gethostbyname (lcase (hostname)))) {
  151.       alarm (i);        /* restore alarms */
  152.                 /* copy address type */
  153.       sin.sin_family = host_name->h_addrtype;
  154.                 /* copy host name */
  155.       strcpy (hostname,host_name->h_name);
  156.                 /* copy host addresses */
  157.       memcpy (&sin.sin_addr,host_name->h_addr,host_name->h_length);
  158.     }
  159.     else {
  160.       alarm (i);        /* restore alarms */
  161.       sprintf (tmp,"No such host as %.80s",host);
  162.       mm_log (tmp,ERROR);
  163.       return NIL;
  164.     }
  165.   }
  166.  
  167.                 /* get a TCP stream */
  168.   if ((sock = socket (sin.sin_family,SOCK_STREAM,pt ? pt->p_proto : 0)) < 0) {
  169.     sprintf (tmp,"Unable to create TCP socket: %s",strerror (errno));
  170.     mm_log (tmp,ERROR);
  171.     return NIL;
  172.   }
  173.   flgs = fcntl (sock,F_GETFL,0);/* get current socket flags */
  174.   fcntl (sock,F_SETFL,flgs | FNDELAY);
  175.                 /* open connection */
  176.   while ((i = connect (sock,(struct sockaddr *) &sin,sizeof (sin))) < 0 &&
  177.      errno == EINTR);
  178.   if (i < 0) switch (errno) {    /* failed? */
  179.   case EAGAIN:            /* DG brain damage */
  180.   case EINPROGRESS:
  181.   case EISCONN:
  182.   case EADDRINUSE:
  183.     break;            /* well, not really, it was interrupted */
  184.   default:
  185.     sprintf (tmp,"Can't connect to %.80s,%d: %s",hostname,port,
  186.          strerror (errno));
  187.     mm_log (tmp,ERROR);
  188.     close (sock);        /* flush socket */
  189.     return NIL;
  190.   }
  191.   FD_SET (sock,&fds);        /* block for error or writeable */
  192.   FD_SET (sock,&efds);
  193.   while (((i = select (sock+1,0,&fds,&efds,tmo.tv_sec ? &tmo : 0)) < 0) &&
  194.      (errno == EINTR));
  195.   if (i > 0) {            /* success, make sure really connected */
  196.     fcntl (sock,F_SETFL,flgs);    /* restore blocking status */
  197. #ifndef SOLARISKERNELBUG
  198.                 /* get socket status */
  199.     while ((i = read (sock,tmp,0)) < 0 && errno == EINTR);
  200.     if (!i) i = 1;        /* make success if the read is OK */
  201. #endif
  202.   }    
  203.   if (i <= 0) {            /* timeout or error? */
  204.     sprintf (tmp,"Can't connect to %.80s,%d: %s",hostname,port,
  205.          strerror (i ? errno : ETIMEDOUT));
  206.     mm_log (tmp,ERROR);
  207.     close (sock);        /* flush socket */
  208.     return NIL;
  209.   }
  210.  
  211.                 /* create TCP/IP stream */
  212.   stream = (TCPSTREAM *) fs_get (sizeof (TCPSTREAM));
  213.                 /* copy official host name */
  214.   stream->host = cpystr (hostname);
  215.                 /* get local name */
  216.   gethostname (tmp,MAILTMPLEN-1);
  217.   stream->localhost = cpystr ((host_name = gethostbyname (tmp)) ?
  218.                   host_name->h_name : tmp);
  219.   stream->port = port;        /* port number */
  220.                 /* init sockets */
  221.   stream->tcpsi = stream->tcpso = sock;
  222.   stream->ictr = 0;        /* init input counter */
  223.   return stream;        /* return success */
  224. }
  225.   
  226. /* TCP/IP authenticated open
  227.  * Accepts: host name
  228.  *        service name
  229.  *        returned user name buffer
  230.  * Returns: TCP/IP stream if success else NIL
  231.  */
  232.  
  233. TCPSTREAM *tcp_aopen (char *host,char *service,char *usrbuf)
  234. {
  235.   TCPSTREAM *stream = NIL;
  236.   struct hostent *host_name;
  237.   char *user = (char *) mail_parameters (NIL,GET_USERNAMEBUF,NIL);
  238.   char hostname[MAILTMPLEN];
  239.   int i;
  240.   int pipei[2],pipeo[2];
  241.   struct timeval tmo;
  242.   fd_set fds,efds;
  243.   if (!(tmo.tv_sec = rshtimeout)) return NIL;
  244.   tmo.tv_usec = 0;
  245.   FD_ZERO (&fds);        /* initialize selection vector */
  246.   FD_ZERO (&efds);        /* handle errors too */
  247.   /* The domain literal form is used (rather than simply the dotted decimal
  248.      as with other Unix programs) because it has to be a valid "host name"
  249.      in mailsystem terminology. */
  250.                 /* look like domain literal? */
  251.   if (host[0] == '[' && host[i = (strlen (host))-1] == ']') {
  252.     strcpy (hostname,host+1);/* yes, copy without brackets */
  253.     hostname[i-1] = '\0';
  254.   }
  255.                 /* note that Unix requires lowercase! */
  256.   else if (host_name = gethostbyname (lcase (strcpy (hostname,host))))
  257.     strcpy (hostname,host_name->h_name);
  258.                 /* make command pipes */
  259.   if (pipe (pipei) < 0) return NIL;
  260.   if (pipe (pipeo) < 0) {
  261.     close (pipei[0]); close (pipei[1]);
  262.     return NIL;
  263.   }
  264.   if ((i = fork ()) < 0) {    /* make inferior process */
  265.     close (pipei[0]); close (pipei[1]);
  266.     close (pipeo[0]); close (pipeo[1]);
  267.     return NIL;
  268.   }
  269.   if (!i) {            /* if child */
  270.     if (!fork ()) {        /* make grandchild so it's inherited by init */
  271.       int maxfd = max (20,max (max(pipei[0],pipei[1]),max(pipeo[0],pipeo[1])));
  272.       dup2 (pipei[1],1);    /* parent's input is my output */
  273.       dup2 (pipei[1],2);    /* parent's input is my error output too */
  274.       dup2 (pipeo[0],0);    /* parent's output is my input */
  275.                 /* close all unnecessary descriptors */
  276.       for (i = 3; i <= maxfd; i++) close (i);
  277.       setpgrp (0,getpid ());    /* be our own process group */
  278.       if (user && *user)    /* now run it */
  279.     execl (RSHPATH,RSH,hostname,"-l",user,"exec",service,0);
  280.       execl (RSHPATH,RSH,hostname,"exec",service,0);
  281.     }
  282.     _exit (1);            /* child is done */
  283.   }
  284.  
  285.   grim_pid_reap (i,NIL);    /* reap child; grandchild now owned by init */
  286.   close (pipei[1]);        /* close child's side of the pipes */
  287.   close (pipeo[0]);
  288.                 /* create TCP/IP stream */
  289.   stream = (TCPSTREAM *) fs_get (sizeof (TCPSTREAM));
  290.                 /* copy official host name */
  291.   stream->host = cpystr (hostname);
  292.                 /* get local name */
  293.   gethostname (hostname,MAILTMPLEN-1);
  294.   stream->localhost = cpystr ((host_name = gethostbyname (hostname)) ?
  295.                   host_name->h_name : hostname);
  296.   stream->tcpsi = pipei[0];    /* init sockets */
  297.   stream->tcpso = pipeo[1];
  298.   stream->ictr = 0;        /* init input counter */
  299.   stream->port = 0xffffffff;    /* no port number */
  300.   FD_SET (stream->tcpsi,&fds);    /* set bit in selection vector */
  301.   FD_SET (stream->tcpsi,&efds);    /* set bit in error selection vector */
  302.   while (((i = select (stream->tcpsi+1,&fds,0,&efds,&tmo)) < 0) &&
  303.      (errno == EINTR));
  304.   if (i <= 0) {            /* timeout or error? */
  305.     mm_log (i ? "error in rsh to IMAP server" : "rsh to IMAP server timed out",
  306.         WARN);
  307.     tcp_close (stream);        /* punt stream */
  308.     stream = NIL;
  309.   }
  310.                 /* return user name */
  311.   strcpy (usrbuf,(user && *user) ? user : myusername ());
  312.   return stream;        /* return success */
  313. }
  314.  
  315. /* TCP/IP receive line
  316.  * Accepts: TCP/IP stream
  317.  * Returns: text line string or NIL if failure
  318.  */
  319.  
  320. char *tcp_getline (TCPSTREAM *stream)
  321. {
  322.   int n,m;
  323.   char *st,*ret,*stp;
  324.   char c = '\0';
  325.   char d;
  326.                 /* make sure have data */
  327.   if (!tcp_getdata (stream)) return NIL;
  328.   st = stream->iptr;        /* save start of string */
  329.   n = 0;            /* init string count */
  330.   while (stream->ictr--) {    /* look for end of line */
  331.     d = *stream->iptr++;    /* slurp another character */
  332.     if ((c == '\015') && (d == '\012')) {
  333.       ret = (char *) fs_get (n--);
  334.       memcpy (ret,st,n);    /* copy into a free storage string */
  335.       ret[n] = '\0';        /* tie off string with null */
  336.       return ret;
  337.     }
  338.     n++;            /* count another character searched */
  339.     c = d;            /* remember previous character */
  340.   }
  341.                 /* copy partial string from buffer */
  342.   memcpy ((ret = stp = (char *) fs_get (n)),st,n);
  343.                 /* get more data from the net */
  344.   if (!tcp_getdata (stream)) fs_give ((void **) &ret);
  345.                 /* special case of newline broken by buffer */
  346.   else if ((c == '\015') && (*stream->iptr == '\012')) {
  347.     stream->iptr++;        /* eat the line feed */
  348.     stream->ictr--;
  349.     ret[n - 1] = '\0';        /* tie off string with null */
  350.   }
  351.                 /* else recurse to get remainder */
  352.   else if (st = tcp_getline (stream)) {
  353.     ret = (char *) fs_get (n + 1 + (m = strlen (st)));
  354.     memcpy (ret,stp,n);        /* copy first part */
  355.     memcpy (ret + n,st,m);    /* and second part */
  356.     fs_give ((void **) &stp);    /* flush first part */
  357.     fs_give ((void **) &st);    /* flush second part */
  358.     ret[n + m] = '\0';        /* tie off string with null */
  359.   }
  360.   return ret;
  361. }
  362.  
  363. /* TCP/IP receive buffer
  364.  * Accepts: TCP/IP stream
  365.  *        size in bytes
  366.  *        buffer to read into
  367.  * Returns: T if success, NIL otherwise
  368.  */
  369.  
  370. long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *buffer)
  371. {
  372.   unsigned long n;
  373.   char *bufptr = buffer;
  374.   while (size > 0) {        /* until request satisfied */
  375.     if (!tcp_getdata (stream)) return NIL;
  376.     n = min (size,stream->ictr);/* number of bytes to transfer */
  377.                 /* do the copy */
  378.     memcpy (bufptr,stream->iptr,n);
  379.     bufptr += n;        /* update pointer */
  380.     stream->iptr +=n;
  381.     size -= n;            /* update # of bytes to do */
  382.     stream->ictr -=n;
  383.   }
  384.   bufptr[0] = '\0';        /* tie off string */
  385.   return T;
  386. }
  387.  
  388. /* TCP/IP receive data
  389.  * Accepts: TCP/IP stream
  390.  * Returns: T if success, NIL otherwise
  391.  */
  392.  
  393. long tcp_getdata (TCPSTREAM *stream)
  394. {
  395.   int i;
  396.   fd_set fds,efds;
  397.   struct timeval tmo;
  398.   time_t t = time (0);
  399.   tmo.tv_sec = tcptimeout_read;
  400.   tmo.tv_usec = 0;
  401.   FD_ZERO (&fds);        /* initialize selection vector */
  402.   FD_ZERO (&efds);        /* handle errors too */
  403.   if (stream->tcpsi < 0) return NIL;
  404.   while (stream->ictr < 1) {    /* if nothing in the buffer */
  405.     FD_SET (stream->tcpsi,&fds);/* set bit in selection vector */
  406.     FD_SET(stream->tcpsi,&efds);/* set bit in error selection vector */
  407.     errno = NIL;        /* block and read */
  408.     while (((i = select (stream->tcpsi+1,&fds,0,&efds,tmo.tv_sec ? &tmo:0))<0)
  409.        && (errno == EINTR));
  410.     if (!i) {            /* timeout? */
  411.       if (tcptimeout && ((*tcptimeout) (time (0) - t))) continue;
  412.       else return tcp_abort (stream);
  413.     }
  414.     if (i < 0) return tcp_abort (stream);
  415.     while (((i = read (stream->tcpsi,stream->ibuf,BUFLEN)) < 0) &&
  416.        (errno == EINTR));
  417.     if (i < 1) return tcp_abort (stream);
  418.     stream->iptr = stream->ibuf;/* point at TCP buffer */
  419.     stream->ictr = i;        /* set new byte count */
  420.   }
  421.   return T;
  422. }
  423.  
  424. /* TCP/IP send string as record
  425.  * Accepts: TCP/IP stream
  426.  *        string pointer
  427.  * Returns: T if success else NIL
  428.  */
  429.  
  430. long tcp_soutr (TCPSTREAM *stream,char *string)
  431. {
  432.   return tcp_sout (stream,string,(unsigned long) strlen (string));
  433. }
  434.  
  435.  
  436. /* TCP/IP send string
  437.  * Accepts: TCP/IP stream
  438.  *        string pointer
  439.  *        byte count
  440.  * Returns: T if success else NIL
  441.  */
  442.  
  443. long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size)
  444. {
  445.   int i;
  446.   fd_set fds;
  447.   struct timeval tmo;
  448.   time_t t = time (0);
  449.   tmo.tv_sec = tcptimeout_write;
  450.   tmo.tv_usec = 0;
  451.   FD_ZERO (&fds);        /* initialize selection vector */
  452.   if (stream->tcpso < 0) return NIL;
  453.   while (size > 0) {        /* until request satisfied */
  454.     FD_SET (stream->tcpso,&fds);/* set bit in selection vector */
  455.     errno = NIL;        /* block and write */
  456.     while (((i = select (stream->tcpso+1,0,&fds,0,tmo.tv_sec ? &tmo : 0)) < 0)
  457.        && (errno == EINTR));
  458.     if (!i) {            /* timeout? */
  459.       if (tcptimeout && ((*tcptimeout) (time (0) - t))) continue;
  460.       else return tcp_abort (stream);
  461.     }
  462.     if (i < 0) return tcp_abort (stream);
  463.     while (((i = write (stream->tcpso,string,size)) < 0) &&
  464.        (errno == EINTR));
  465.     if (i < 0) return tcp_abort (stream);
  466.     size -= i;            /* how much we sent */
  467.     string += i;
  468.   }
  469.   return T;            /* all done */
  470. }
  471.  
  472. /* TCP/IP close
  473.  * Accepts: TCP/IP stream
  474.  */
  475.  
  476. void tcp_close (TCPSTREAM *stream)
  477. {
  478.   tcp_abort (stream);        /* nuke the stream */
  479.                 /* flush host names */
  480.   fs_give ((void **) &stream->host);
  481.   fs_give ((void **) &stream->localhost);
  482.   fs_give ((void **) &stream);    /* flush the stream */
  483. }
  484.  
  485.  
  486. /* TCP/IP abort stream
  487.  * Accepts: TCP/IP stream
  488.  * Returns: NIL always
  489.  */
  490.  
  491. long tcp_abort (TCPSTREAM *stream)
  492. {
  493.   int i;
  494.   if (stream->tcpsi >= 0) {    /* no-op if no socket */
  495.     close (stream->tcpsi);    /* nuke the socket */
  496.     if (stream->tcpsi != stream->tcpso) close (stream->tcpso);
  497.     stream->tcpsi = stream->tcpso = -1;
  498.   }
  499.   return NIL;
  500. }
  501.  
  502. /* TCP/IP get host name
  503.  * Accepts: TCP/IP stream
  504.  * Returns: host name for this stream
  505.  */
  506.  
  507. char *tcp_host (TCPSTREAM *stream)
  508. {
  509.   return stream->host;        /* return host name */
  510. }
  511.  
  512.  
  513. /* TCP/IP return port for this stream
  514.  * Accepts: TCP/IP stream
  515.  * Returns: port number for this stream
  516.  */
  517.  
  518. long tcp_port (TCPSTREAM *stream)
  519. {
  520.   return stream->port;        /* return port number */
  521. }
  522.  
  523.  
  524. /* TCP/IP get local host name
  525.  * Accepts: TCP/IP stream
  526.  * Returns: local host name
  527.  */
  528.  
  529. char *tcp_localhost (TCPSTREAM *stream)
  530. {
  531.   return stream->localhost;    /* return local host name */
  532. }
  533.  
  534.  
  535. /* TCP/IP get server host name
  536.  * Accepts: pointer to destination
  537.  * Returns: string pointer if got results, else NIL
  538.  */
  539.  
  540. char *tcp_clienthost (char *dst)
  541. {
  542.   struct hostent *hn;
  543.   struct sockaddr_in from;
  544.   int fromlen = sizeof (from);
  545.   if (getpeername (0,(struct sockaddr *) &from,&fromlen)) return "UNKNOWN";
  546.   strncpy (dst,(hn = gethostbyaddr ((char *) &from.sin_addr,
  547.                    sizeof (struct in_addr),from.sin_family)) ?
  548.        hn->h_name : inet_ntoa (from.sin_addr),80);
  549.   return dst;
  550. }
  551.